package org.munoz.client;


import java.util.ArrayList;

import com.google.gwt.appengine.channel.client.ChannelError;
import com.google.gwt.appengine.channel.client.ChannelFactoryImpl;
import com.google.gwt.appengine.channel.client.Socket;
import com.google.gwt.appengine.channel.client.SocketListener;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsonUtils;
//import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DragOverEvent;
import com.google.gwt.event.dom.client.DragOverHandler;
import com.google.gwt.event.dom.client.DragStartEvent;
import com.google.gwt.event.dom.client.DragStartHandler;
import com.google.gwt.event.dom.client.DropEvent;
import com.google.gwt.event.dom.client.DropHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ButtonBase;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

import org.munoz.client.gwtfb.sdk.FBCore;
import org.munoz.shared.Card;
import org.munoz.shared.Deck;
import org.munoz.shared.GameResult;
import org.munoz.shared.LoginInfo;
import org.munoz.shared.Move;
import org.munoz.shared.Rank;
import org.munoz.shared.State;
import org.munoz.shared.StateChanger;
import org.munoz.shared.Suit;

/**
 * @author jpm
 * This class takes care of updating the view and dealing with the logic of the game
 */
public class Presenter{
    public interface View {
            /**
             * @return the reset button (used for ClickHandlers)
             */
            HasClickHandlers getResetButton();
            
            /**
             * @return the automatch button.
             */
//            HasClickHandlers getAutomatchButton();
            Button getAutomatchButton();
            
            /**
             * @return the play button
             */
            HasClickHandlers getPlayButton();
            
            /**
             * @return the logout button
             */
            HasClickHandlers getLogoutButton();
            
            /**
             * @return textBox with adversary's email.
             */
            TextBox getEmailOpponentTextBox();
            
            /**
             * @return the list of active matches
             */
            ListBox getListOfMatches();
            
            /**
             * @return selected match from active matches
             */
            Long getSelectedMatch();
            
            /**
             * @return the delete button
             */
            HasClickHandlers getDeleteButton();
            
            /**
             * @return the card that was requested by the user
             */
            Label getCardRequested();
            
            /**
             * @return a copy of the game images
             */
            GameImages getGameImages();

            /**
             * Plays sound of cards being shuffled 
             */
            void playShuffleDeckSound();
            
            /**
             * Plays intro sound. 
             */
            void playIntroSound();
            
            /**
             * @return true if Local Storage is supported
             */
            boolean isLocalStorageSupported();
            
            /**
             * @param key
             * @return the state saved previously
             */
            String readValueFromLocalStorage(String key);
            
            /**
             * Saves state to local storage
             * @param key
             * @param value
             */
            void writeValueToLocalStorage(String key, String value);
            
            /**
             * Displays cards in player's hand
             * @param hand
             */
            void setCardsInHand(Deck hand); 
            
            /**
             * @return the images of the cards in player's hand
             */
            Image[] getCardsInHandImages();
			
            /**
             * Sets the information of the game to be displayed to the user
             * @param gameResult
             * @param cardsInDeck
             * @param gameInitialized
             */
            void setStateOfGame(GameResult gameResult, int cardsInDeck, boolean gameInitialized, boolean loggedIn);
			
            /**
             * Sets the cards placed on the table by the users. 
             * @param state
             */
            void setGroupOfCardsInTable(State state);
			
            /**
             * clears the view and shows the state of the game
             * @param state
             */
            void clearView(State state);
			
            /**
             * @param card c
             * @return returns a bigger image of the card
             */
            ImageResource imageHelper(Card c);
			
            /**
             * Displays the name of the opponent
             * @param opponentName
             */
            void displayOpName(String opponentName);
			
            /**
             * Sets the turn of the game. 
             * @param turn
             * @param userTurn
             */
            void setWhoseTurn(int turn, int userTurn);
            
            /**Sets the Username and email in UI
             * @param nameAndEmail
             */
            void setUserNameAndEmailUI(String nameAndEmail);
            
            /** Sets opponent's Name in UI
             * @param opponent
             */
            void setOpponentNameUI(String opponent);
            
            /** Sets match id in UI
             * @param matchId
             */
            void setMatchIdUI(String matchId);
            
            /**
             * @return the label with a message of the number of cards in deck
             */
            Label getMessageCardsInDeck();
            
            // HW#8
            
            /**
             * Sets the starting date of the match
             * @param date
             */
            void setStartDate(String date);
            
            /**
             * Sets the ranking of the player
             * @param ranking
             */
            void setRankingUI(String ranking);
            
            /**
             * Disable board if player isn't logged in
             */
            void disableBoard();
            
            /**
             * Enable board functionality
             */
            void enableBoard();
            
            // #HW 9
            /**
             * @return the button to play against the computer.
             */
            Button getAIButton();
            
            /**
             * Enables only the AI option
             */
            void enableOnlyAI();
            
            /**
             * @return the facebook button
             */
            Button getFriendsButton();
                        
            
    }
    
    private View view;
    private State state;
    private State previousState;
    private StateChanger stateChanger;
//    private boolean initializedHistory;
    private boolean gameInitialized;
    private GoFishServiceAsync goFishSvc;
    GoFishMessages msgInt = GWT.create(GoFishMessages.class);
    private LoginInfo loginInfo = null;
    private String userEmail = "";
    private int userTurn = 99;
    private int opponentsTurn = 99;
    private String initialState;
    private String opponentName;
    private Long matchId;

    boolean waitingForServerResponse;
    
    boolean playingAi;
    AIPlayer ai;
    
    FacebookDialog fbDialog; 
    FBCore fbCore = new FBCore();
    String fbAdvId = "";
    String fbAdvName = "";
    
    /**
     * Creates a new presenter object with a new empty State. 
     */
    public Presenter(/*LoginServiceAsync loginS,*/ GoFishServiceAsync goFS, View graphics, LoginInfo loginI) {
//    	state = new State();
    	playingAi = false;
        stateChanger = new StateChanger();
//        initialState = State.initialState();
//        initializedHistory = false;
        initView(graphics);
        gameInitialized = false;
//        System.out.println("Presenter created");
        goFishSvc = goFS;
        
        loginInfo = loginI;
        
        if(loginInfo.isLoggedIn()){
        	login();
        }
    }
    
    
        
    /**
     * Uses RPC to login into the server. 
     */
    public void login(){
    	goFishSvc.login("GoFish.html", loginInfo, new AsyncCallback<LoginInfo>() { // TODO: Change "GoFish.html" for Window.Location.getHref()
            public void onFailure(Throwable error) {
            	GWT.log(msgInt.errorLoginIn() + " " + error.getMessage());
            }
            public void onSuccess(LoginInfo cp) {
//              loginInfo = result;
              if(loginInfo.isLoggedIn()) {
//            	  goFishSvc.loadMatches(loginInfo, loadMatchesCallback); // I am loading the matches below.
            	  Window.alert(msgInt.welcome() + " " + loginInfo.getFBUserName()); // loginInfo.getEmailAddress());
//            	  userEmail = result.getEmailAddress();
            	  view.setUserNameAndEmailUI(msgInt.user() + " " + loginInfo.getFBUserName()); //userEmail); // TODO FIX this
            	  view.setRankingUI("Ranking: " + cp.getGlickoRange()); // loginInfo.getGlickoRange());
            	  loginInfo.setGlickoRange(cp.getGlickoRange());
            	  loginInfo.setToken(cp.getToken());
                  Socket socket = new ChannelFactoryImpl().createChannel(cp.getToken()).open(new SocketListener() {
                  	@Override
                  	public void onOpen() {
//                  		Window.alert("Channel Oppened");
                  		view.enableBoard();
                  		//setEmail(loginInfo.getEmailAddress()); CommentedOut for Facebook HW.
                  		setEmail(loginInfo.getUserFBId());
                  		goFishSvc.loadMatches(loginInfo, loadMatchesCallback);
                  	}
                  	@Override
                  	public void onMessage(String message) {
//                  		Window.alert("Message Received!" + message);
                  		if(message.length()==0){
                  			return;
                  		}
                  		String [] checkMessage = message.split("[#]"); // Old splitting of messages
                  		if(checkMessage[0].compareTo("newgame")==0){
            				gameInitialized = true;
                  			System.out.println("Presenter: newgame received by " + userEmail);
                  			matchId = Long.parseLong(checkMessage[1]);
                  			view.setMatchIdUI("Match id: " + matchId);
                  			userTurn = Integer.parseInt(checkMessage[2]);
                  			opponentName = checkMessage[3];
//                  			view.setOpponentNameUI(opponentName);
                  			view.setStartDate(checkMessage[4]);
                  			view.displayOpName(opponentName);
                  			setState(State.unserializeState(checkMessage[5]));
            				opponentsTurn = (userTurn == State.PLAYER1)? State.PLAYER2 : State.PLAYER1;
//            				System.out.println("Presenter: newgame " + userEmail + " received new game against " + opponentName + " " + gameInitialized);
            				goFishSvc.loadMatches(loginInfo, loadMatchesCallback);
                  		}
                  		else if(checkMessage[0].compareTo("opponentmoved") == 0){
//                  			System.out.println(userEmail + " received " + checkMessage);
//                  			System.out.println("Presenter: received opponentmoved " + userEmail);
                  			matchId = Long.parseLong(checkMessage[1]);
//                  			System.out.println("Presenter: matchId : " + matchId);              			
                  			// Other player receives new State (including last move) 
                  			state = State.unserializeState(checkMessage[2]);
//                  			System.out.println("State loaded " + checkMessage[2]);
//                  			System.out.println("State loaded " + state.toString());
//                  			System.out.println("User Turn " + userTurn);
//                  			System.out.println("state.getTurn() " + state.getTurn());
                  			
                  			// In case I want to display what was the card selected. 
//                  			Rank requested = State.getRankFromString(checkMessage[3]);
//                  			System.out.println("Rank requested " + checkMessage[3]);
//                  			try{
//                  				stateChanger.makeMove(state, new Move(state.getTurn(), requested));
//                  			} catch(RuntimeException e){
//                  				System.out.println(e.getMessage());                   				
//                  			}
//                  			System.out.println(userEmail + " State after change " + state.toString());
//                  			System.out.println(userEmail + " received opponent move " + checkMessage[3] + " from " + opponentName);
                  			waitingForServerResponse = false;
                  			updateBoard();
                  		}
                  		else if(checkMessage[0].compareTo("glicko")==0){
                      	  view.setRankingUI("Ranking: " + checkMessage[1]);
                  		}
                  	}

  					@Override
  					public void onError(ChannelError error) {
  						Window.alert("Channel error: " + error.getCode() + " : " + error.getDescription());						
  					}

  					@Override
  					public void onClose() {
  						Window.alert("Channel closed!");
  					}
                  });                        
            	  view.getLogoutButton().addClickHandler(new ClickHandler(){
					@Override
					public void onClick(ClickEvent event) {
						Window.Location.assign(loginInfo.getLogoutUrl());
					}
            	  });
              } else {
            	  LoginPanel lg=  new LoginPanel(loginInfo.getLoginUrl());
            	  lg.center();
              }
            }
          });
    }
    
	/**
	 * Sends a move to the server. 
	 * @param userId2
	 * @param state2
	 */
    private void serverMakeMoveHW7(Rank requested){
//    	String stateBeforeMove = State.serializeState(previousState); //state);
//    	goFishSvc.makeMove(matchId, requested.toString(), stateBeforeMove, new AsyncCallback<Void>(){
    	waitingForServerResponse = true;
    	goFishSvc.makeMove(matchId, requested.toString(), State.serializeState(state), loginInfo, new AsyncCallback<Void>(){

			@Override
			public void onFailure(Throwable caught) {
				Window.alert(caught.getMessage());				
			}

			@Override
			public void onSuccess(Void result) {
//				System.out.println("RPC makeMove returned successful");
			}
    		
    	});
    }
 // Old code HW#6
//	private void serverMakeMove(String userId, String state) {	
		
//		goFishSvc.makeMoveAtServer(userId, state, new AsyncCallback<String>() {
//			@Override
//			public void onFailure(Throwable caught) {
//				Window.alert(caught.getMessage());
//			}
//
//			@Override
//			public void onSuccess(String result) {
//				
//			}
//		});
//	}

	/**
	 * Randomly match the player with a waiting player. If nobody is waiting, then this player waits until somebody else
	 * connects. 
	 * @param userId
	 */
	// Old Code HW#6
//    private void matchPlayer(String userId){
//		if(opponentsTurn != 99 && userTurn != 99){
//			System.out.println("Returned here");
//			return;
//		}
//    	// Set callback
//    	AsyncCallback<String> callback = new AsyncCallback<String>(){
//
//			@Override
//			public void onFailure(Throwable caught) {
//				caught.printStackTrace();
//			}
//
//			@Override
//			public void onSuccess(String result) {
//				int tempTurn = new Integer(result);
//				if(tempTurn == 99){
//					return;
//				}
//				userTurn = tempTurn;
//				gameInitialized = true;
//          		if(userTurn == State.PLAYER2){
//  					serverMakeMove(userEmail, initialState);
//  				}
//				opponentsTurn = (userTurn == State.PLAYER1)? State.PLAYER2 : State.PLAYER1; 
//			}
//    	};
//    	
//    	// Make call
//    	goFishSvc.matchPlayer(userId,initialState, callback);
//	}
    
    /**
     * Sets the email address of the player. 
     * @param id
     */
    public void setEmail(String id){
        this.userEmail=id;
    }
    
    /**
     * For Testing RPC
     * Sends a message to the server to test RPC
     */
    public void messageToServer(){
    	if(goFishSvc == null){
    		goFishSvc = GWT.create(GoFishService.class);
    	}
    	
    	// Set callback
    	AsyncCallback<String> callback = new AsyncCallback<String>(){

			@Override
			public void onFailure(Throwable caught) {
				caught.printStackTrace();
			}

			@Override
			public void onSuccess(String result) {
				System.out.println("Server says " + result);
			}
    	};
    	
    	// Make call
    	goFishSvc.sayHi("test", "test2", callback);
    }
  
    
    /**
     * Initializes the game and tells view to transition to initial state.
     * @param view
     */
    public void initView(View view){
    	this.view = view;
        view.playIntroSound();
        if(loginInfo != null){
        	if(loginInfo.isLoggedIn()){
        		view.enableBoard();
//        		view.setStateOfGame(state.getGameResult(), state.getSizeOfDeck(), gameInitialized, loginInfo.isLoggedIn());
        	}
        }
//		view.setStateOfGame(state.getGameResult(), state.getSizeOfDeck(), gameInitialized);

        // Commented for HW#7
//    	view.getResetButton().addClickHandler(new ClickHandler() {
//            @Override
//            public void onClick(ClickEvent e) {
//            	if(userEmail.compareTo("") != 0 && opponentsTurn == 99){
//            		setWaiting();
//                	matchPlayer(userEmail);
//            	}
//            	else{
//            		Window.alert("Waiting for opponent!");
//            	}
            	
//            	gameInitialized = true;
//              int turn = State.PLAYER1; 
//        		Deck mainDeck = new Deck();
//        		Player p1 = new Player(State.PLAYER1); 
//        		Player p2 = new Player(State.PLAYER2); 
//        		ArrayList<Rank> groupOfCardsPlacedInTable = new ArrayList<Rank>();
//        		mainDeck.restoreDeck();
//        		IntGeneratorJP gen = new IntGeneratorJP();
//        		mainDeck.shuffle(gen);
//        		GameResult gameResult = null;
//        		state = new State(turn, mainDeck, p1, p2, gameResult, groupOfCardsPlacedInTable);
//        		int numberCardsPerPlayer = 7;
//        		state.dealInitialHands(numberCardsPerPlayer);
//        		clearView(state);
//              updateBoard();
        
        // Commented for HW#7
//            }
//    	});
    	
    	final Label target = view.getCardRequested();
    	target.addDragOverHandler(new DragOverHandler(){
			@Override
			public void onDragOver(DragOverEvent event) {
				target.getElement().getStyle().setBackgroundColor("#ffa");
				
			}
    	});
    	target.addDropHandler(new DropHandler(){

			@Override
			public void onDrop(DropEvent event) {
				event.preventDefault();
				String data = event.getData("rank");
//	            target.getElement().getParentElement().getStyle().setBackgroundImage("CLUBS_ACE");            
				//target.setText(data);
				selectCard(State.getRankFromString(data));
				updateBoard();
			}
    		
    	});
    	
    	view.getAutomatchButton().addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				System.out.println("Automatch was called");
				setWaiting();
				//clearView(new State());
				goFishSvc.automatch(loginInfo, new AsyncCallback<Void>(){

					@Override
					public void onFailure(Throwable caught) {
						Window.alert("Problems with automatching " + caught.getMessage());
					}

					@Override
					public void onSuccess(Void result) {
						// Do nothing
					}					
				}); 
				
			}
    		
    	});
    	
    	view.getPlayButton().addClickHandler(new ClickHandler(){

			@Override
			public void onClick(ClickEvent event) {
				String advEmail = getView().getEmailOpponentTextBox().getText();
				if(!advEmail.matches("^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$")){
//						"^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
//				        + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$")){
					Window.alert(msgInt.invalidEmail());
					return;
				}
				goFishSvc.newGameFromEmail(advEmail, loginInfo, new AsyncCallback<Boolean>(){
					@Override
					public void onFailure(Throwable caught) {
						Window.alert("Error" + caught.getMessage());
					}

			        @Override
			        public void onSuccess(Boolean result) {
			        	if (result == false) {
			        		Window.alert(msgInt.userNotFound());
			        	}
			        }  
				});
			}
    		
    	});
    	view.getListOfMatches().addChangeHandler(new ChangeHandler(){
			@Override
			public void onChange(ChangeEvent event) {
				Long matchID = getView().getSelectedMatch();
				goFishSvc.changeMatch(matchID, loginInfo, new AsyncCallback<String>(){
					@Override
					public void onFailure(Throwable caught) {
						Window.alert(msgInt.errorLoadMatch());
					}
					@Override
					public void onSuccess(String result) {
						System.out.println("Changing Match onSuccess: ");
						if(!result.equals("no match")){
							String tokens[] = result.split("#");
							matchId = Long.valueOf(tokens[0]);
							userTurn = Integer.parseInt(tokens[1]);
							opponentName = tokens[2];
//							if(tokens.length > 3){
//							String stateStr = tokens[3];
//							}
							//String status = "Match ID: " + matchId + ";";
							//getView().setOpponentNameUI(opponentName);
							//startGame(stateStr);
							gameInitialized = true;
							getView().setMatchIdUI(msgInt.matchId() + " " + matchId);
							getView().displayOpName(opponentName);
							getView().setStartDate(tokens[3]);
							state = State.unserializeState(tokens[4]);
							setState(state);
            				opponentsTurn = (userTurn == State.PLAYER1)? State.PLAYER2 : State.PLAYER1;
//            				System.out.println(tokens[0] + " " + tokens[1] + " " + tokens[2] + " " + tokens[3]);
						}
					}
				});
			}
    	});
    	view.getDeleteButton().addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				for(int i=0; i<getView().getListOfMatches().getItemCount(); i++){
					if(getView().getListOfMatches().getValue(i).equals(matchId)){
						getView().getListOfMatches().removeItem(i);
						break;
					}
				}
				goFishSvc.deleteMatch(matchId, loginInfo, new AsyncCallback<Void>(){
					@Override
					public void onFailure(Throwable caught) {	
						Window.alert("Error while deleting match " + caught.getMessage());
					}
					@Override
					public void onSuccess(Void result) {
						goFishSvc.loadMatches(loginInfo, loadMatchesCallback);
					}
				});
			}
    	});
    	view.getAIButton().addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				if(playingAi == false){
					getView().disableBoard();
					getView().enableOnlyAI();
					playingAi = true;
					getView().getAIButton().setText(msgInt.aiStop());
					
					// Start game with AI
					startAIGame();
				}
				else{
					playingAi = false;
					getView().setCardsInHand(new Deck());
					state = new State();
					gameInitialized = false;
					getView().enableBoard();
					getView().setStateOfGame(null, 0, gameInitialized, loginInfo.isLoggedIn());
					//updateBoard();
				}
			}
    	});
    	
    	view.getFriendsButton().addClickHandler(new ClickHandler(){

			@Override
			public void onClick(ClickEvent event) {
				selectFBFriends();
			}
    		
    	});
    	
    }
    
    public void selectFBFriends(){
//    	Window.alert("About to show friends");
    	fbDialog = new FacebookDialog();
    	fbDialog.showFriends();
    }
    
    /**
     * Starts a game with the AI
     */
    private void startAIGame(){
    	gameInitialized = true;
    	getView().displayOpName("Robot");
    	state = State.unserializeState(State.initialState());
    	userTurn = State.PLAYER1;
    	ai = new AIPlayer();
    	ai.setPlayer(state.getPlayers()[State.PLAYER2]);
    	updateBoard();
    }
    
	AsyncCallback<String []> loadMatchesCallback = new AsyncCallback<String []>(){
		@Override
		public void onFailure(Throwable caught) {
			Window.alert("Error loading matches " + caught.getMessage());
		}

		@Override
		public void onSuccess(String[] result) {
			getView().getListOfMatches().clear();
			getView().getListOfMatches().addItem(msgInt.selectMatch(), "");
			for(String matchS : result){
				if(matchS == null){
					continue;
				}
				String [] tokens = matchS.split("#");
				String mId = tokens[0];
//				String opponentEmail = tokens[1];
//				String opponentName = tokens[2];
//				String turn = tokens[3];
//				String player1 = tokens[4];
//				String turnText = "Your Turn!";
//				if(opponentName.equals(turn)){
//					turnText = "Opponent's turn";
//				}
//				if(tokens.length > 5){
//					String stateStr = tokens[3];  //[5]
////					State newState = unserializeState(stateStr);
//					// TODO: Check
//					setState(State.unserializeState(stateStr));
//				}
//				}
				
				getView().getListOfMatches().addItem("" + mId, mId + "");
			}
		}
		
	};
    
    /**
     * Clears view and creates new state changer
     * @param state
     */
    public void startGame(String state){
    	stateChanger = new StateChanger();
    	if(state != null){
    		view.clearView(State.unserializeState(state));
    	}else{
    		view.clearView(State.unserializeState(State.initialState()));
    	}
    }
    
    /**
     * @return the view
     */
    View getView(){
    	return view;
    }
    
    /**
     * Sets waiting message in view.
     */
    private void setWaiting(){
    	view.setWhoseTurn(-1, -1);
    }
    
    /**
     * Helper function to clear the elements of the view.
     * @param state
     */
    public void clearView(State state){
		view.playShuffleDeckSound();
    	view.clearView(state);
    }
    
    /**
     * Updates the images of the cards in possession of each player.
     * @param view
     */
    public void updateHandView(View view) {    
    	Image[] cardsInHand = view.getCardsInHandImages();
        for(int i=0; i<cardsInHand.length; i++){
        	Rank rCh = null;
        	Suit su = null;
        	
        	String [] it = cardsInHand[i].getTitle().split(" ");
        	final String suitS = it[0];
        	final String rankS = it[1];
        	for(Rank r : Rank.VALUES){
        		if(r.toString().compareTo(rankS)==0){
        			rCh = r;
        		}
        	}
        	final Rank rankMove = rCh;
//        	cardsInHand[i].addClickHandler(new ClickHandler(){
//				@Override
//				public void onClick(ClickEvent event) {
//					selectCard(rankMove);
//				}
//        	});
        	final Image img = cardsInHand[i];
        	img.addDragStartHandler(new DragStartHandler(){
				@Override
				public void onDragStart(DragStartEvent event) {
		        	//img.setResource(bigger(suitS + "_" + rankS)); // TODO
					event.setData("rank", rankMove.toString());
					Element cImg = img.getElement();
					//cImg.getStyle().setHeight(150,Unit.PX);
					cImg.getStyle().setBorderWidth(4, Unit.PX);
			        event.getDataTransfer().setDragImage(cImg, 0, 0);
			        //img.setVisible(false);
				}
			});        	
        }
    }
    
    /**
     * Returns a scaled (bigger) image of the card.
     * @param s
     * @return
     */
    public ImageResource bigger(String s){
    	return view.getGameImages().backOfCard(); // TODO: Return bigger card.
    }

    /**
     * Record the current state in the browser history (if enabled) and redraw the board
     */
    public void updateBoard() {
//            if (initializedHistory){
//                    History.newItem(serializeState(state));
//            }
//            else
        setState(state);
        
        if(playingAi && state.getTurn() == State.PLAYER2){
    		Timer t = new Timer() {
				@Override
				public void run() {
					selectCardAI(ai.requestCardTurn());
				}
    		};
    	    t.schedule(5000);
    	}
    }
    
    
    /**
     * Set the state of the board, and redraw it
     * @param state
     */
    public void setState(State state) {
            this.state = state;
            view.setStateOfGame(state.getGameResult(), state.getSizeOfDeck(), gameInitialized, loginInfo.isLoggedIn());
//            if(state.getGameResult() == null){
            if(state.getGameResult() == null && userEmail.compareTo("") !=0){
                view.setWhoseTurn(state.getTurn(), userTurn);
            }
            else if(state.getGameResult() != null && gameInitialized){
            		view.setWhoseTurn(80, 100);
            }
            else{
                	view.setWhoseTurn(-2, 99);
            }
            //view.setCardsInHand(state.getPlayers()[state.getTurn()].getHand());
            view.setCardsInHand(state.getPlayers()[userTurn].getHand());
            view.setGroupOfCardsInTable(state);
            updateHandView(view);
            view.getCardRequested().getElement().getStyle().setBackgroundColor("#dddddd");
            view.getCardRequested().setText(msgInt.dropHere());
            view.getMessageCardsInDeck().setText(msgInt.deckMessage());
    }
    
    /**
     *  Adds the ValueChangeHandler responsible for traversing the browser history

     */
//    public void initializeHistory() {
//            History.addValueChangeHandler(new ValueChangeHandler<String> () {
//                    @Override
//                    public void onValueChange(ValueChangeEvent<String> event) {
//                            String historyToken = event.getValue();
//                            setState(unserializeState(historyToken));
//                    }
//            });
//            String startState = History.getToken();
//            System.out.println("Start State: " + startState);
//            setState(unserializeState(startState));
//            initializedHistory = true;
//    }
    
    
    
 
    
    /**
     * Returns the state of the game
     * @return
     */
    public State getState(){
    	return state;
    }
    
    /**
     * Call StateChanger with the rank that was requested by the player. 
     * @param requested
     */
    public void selectCard(Rank requested){
    	if(state.getGameResult() != null){
    		Window.alert(msgInt.chooseOptions());
    	}
    	if(state.getTurn() != userTurn){
    		Window.alert(msgInt.notYourTurn());
    		return;
    	}
    	
    	if(playingAi){
    		stateChanger.makeMove(state, new Move(state.getTurn(), requested));
    		if(state.getTurn() != ai.getPlayer().getId()){
    			ai.addMemory(requested);
    		}
    		//updateBoard();
    		return;
    	}
    	
    	if(waitingForServerResponse){
    		Window.alert(msgInt.waitingForServer());
    		return;
    	}
//    	System.out.println("selectCard " + requested.toString());
//    	previousState = state.copy();
//    	stateChanger.makeMove(state, new Move(state.getTurn(), requested));
    	//serverMakeMove(userEmail, State.serializeState(state)); // HW#6
    	serverMakeMoveHW7(requested);
    	//updateBoard();
    }
    
    /**
     * Runs algorithm for AI to request a card
     * @param requested
     */
    public void selectCardAI(Rank requested){
    	if(playingAi){
    		System.out.println(requested.toString());
    		stateChanger.makeMove(state, new Move(state.getTurn(), requested));
    		updateBoard();
    		return;
    	}
    }



	/**
	 * Creates a new match with a Facebook friend
	 * @param fId
	 * @param fName
	 */
	public void newGameWithFBFriend(String fId, String fName) {
//		Window.alert("About to start a game with " + fId + " " + fName);
		fbDialog.hide();
		goFishSvc.newGameFromFBId(fId, loginInfo, new AsyncCallback<Boolean>(){
			@Override
			public void onFailure(Throwable caught) {
				Window.alert("Error" + caught.getMessage());
			}

	        @Override
	        public void onSuccess(Boolean result) {
	        	if (result == false) {
	        		Window.alert(msgInt.userNotFound());
	        	}
	        }  
		});

	}
	
	/**
	 * Class used to show facebook friends
	 * @author jpm
	 *
	 */
	private class FacebookDialog extends DialogBox{
	    private final FriendsUI friendsUI; 
		private final Button inviteButton = new Button(msgInt.inviteFriends());
		
		public FacebookDialog() {
			setModal(true);
			setAutoHideEnabled(true);
			setGlassEnabled(true);
//        	setGlassStyleName(style.glass());
			setAnimationEnabled(true);
			friendsUI = new FriendsUI(Presenter.this);
		}	
		
		/**
		 * Get friends that are connected
		 */
		public void showFriends() {
	        JavaScriptObject param = JsonUtils.safeEval("{\"method\": \"fql.query\", "
	                + "\"query\": \"select uid, name, pic_big from user where is_app_user = 1 and uid in (SELECT uid2 FROM friend WHERE uid1 = me())\"}");
	        fbCore.api(param, new AsyncCallback<JavaScriptObject>() {
	        	@Override
	        	public void onFailure(Throwable caught) {
	        		Window.alert("Error showing friends");
	        	}

	        	@Override
	        	public void onSuccess(JavaScriptObject result) {
	        		processFriends(result);
	        	}
	        });
		}
		/**
		 * Display friend's info and pictures. 
		 * @param result
		 */
		private void processFriends(JavaScriptObject result){
			final VerticalPanel mainPanel = new VerticalPanel();
	        final HorizontalPanel dispFriends = new HorizontalPanel();
			JSONObject js = new JSONObject(result);
	        if (js.size() == 0) {
	                Label noFriends = new Label(msgInt.noFriendsPlay());
	                mainPanel.add(noFriends);
	        } 
	        else {
	                CellList<FriendData> friendList = new CellList<FriendData>(
	                                friendsUI);//getFriendsUI());
	                ArrayList<FriendData> friends = new ArrayList<FriendData>();
	                for (int i = 0; i < js.size(); i++) {
	                        FriendData friend = new FriendData();
	                        JSONObject temp = js.get(String.valueOf(i)).isObject();
	                        friend.fId = temp.get("uid").isString().stringValue();
	                        friend.fName = temp.get("name").isString().stringValue();
	                        friend.fPic = temp.get("pic_big").isString().stringValue();
	                        friends.add(friend);
	                }
	                friendList.setRowData(friends);
	                mainPanel.add(friendList);
	        }
	        inviteButton.addClickHandler(new ClickHandler() {
	                @Override
	                public void onClick(ClickEvent event) {
	                        FacebookDialog.this.setAutoHideEnabled(false);
	                        JavaScriptObject param = JsonUtils
	                                        .safeEval("{\"method\": \"apprequests\", "
	                                                        + "\"title\": \"Invite your friend to play with you together!\", "
	                                                        + "\"message\": \"Hey guys, just check this out!\"}");
	                        fbCore.ui(param,
	                                        new AsyncCallback<JavaScriptObject>() {
	                                                @Override
	                                                public void onFailure(Throwable caught) {
	                                                	Window.alert("Failure getting friends' info");
	                                                }
//
	                                                @Override
	                                                public void onSuccess(
	                                                                JavaScriptObject result) {
	                                                        FacebookDialog.this.hide();
	                                                }
	                                        });
	                }
	        });
	        dispFriends.add(inviteButton);
	        dispFriends.setCellVerticalAlignment(inviteButton,
	                        HasVerticalAlignment.ALIGN_MIDDLE);
	        mainPanel.add(dispFriends);
	        setWidget(mainPanel);
	        FacebookDialog.super.center();
	        FacebookDialog.this.setPopupPosition(getPopupLeft(), getPopupTop()/2);
	        FacebookDialog.this.show();
		}
	}
}
